from fastapi import FastAPI, Request, Response, HTTPException, status, Form, UploadFile, File, Body
from fastapi.responses import RedirectResponse, JSONResponse, FileResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
import sqlite3
import bcrypt
import os
import uuid
from pathlib import Path
from typing import Optional, List, Dict, Any
import shutil
from urllib.parse import unquote

# ========== 初始化配置  ==========
app = FastAPI(
    docs_url="/api-docs",  # 自定义 Swagger 路径
    redoc_url="/api-redoc"  # 自定义 ReDoc 路径
)

PORT = 3009
BASE_DIR = Path(__file__).parent
UPLOAD_DIR = BASE_DIR / "uploads"
DB_FILE = BASE_DIR / "asset-management.db"
os.makedirs(UPLOAD_DIR, exist_ok=True)

# 跨域配置 (确保与前端交互无阻碍)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ========== 静态文件配置  ==========
app.mount("/public", StaticFiles(directory=BASE_DIR / "public"), name="public")
app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR), name="uploads")
app.mount("/bootstrap", StaticFiles(directory=BASE_DIR / "node_modules/bootstrap/dist"), name="bootstrap")
app.mount("/chart.js", StaticFiles(directory=BASE_DIR / "node_modules/chart.js/dist"), name="chart.js")
app.mount("/xlsx", StaticFiles(directory=BASE_DIR / "node_modules/xlsx/dist"), name="xlsx")
app.mount("/vue", StaticFiles(directory=BASE_DIR / "node_modules/vue/dist"), name="vue")

# ========== 会话管理  ==========
sessions = {}

def get_session(request: Request):
    session_id = request.cookies.get("session_id")
    return sessions.get(session_id)

def create_session(user_data: dict):
    session_id = str(uuid.uuid4())
    sessions[session_id] = user_data
    return session_id

def delete_session(session_id: str):
    if session_id in sessions:
        del sessions[session_id]

# ========== 中间件  ==========
def require_login(request: Request):
    if request.url.path in ["/api-docs", "/openapi.json", "/api-redoc"]:
        return None  # 不拦截这些路径
    
    if not get_session(request):
        response = RedirectResponse(url="/login", status_code=status.HTTP_302_FOUND)
        raise HTTPException(
            status_code=status.HTTP_302_FOUND,
            detail="Not logged in",
            headers={"Location": "/login"}
        )
    return get_session(request)

def require_admin(request: Request):
    session = require_login(request)
    if session["role"] != "admin":
        raise HTTPException(status_code=403, detail="无权访问")
    return session

# ========== 数据库初始化  ==========
def init_db():
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    
    # 用户表
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT UNIQUE NOT NULL,
            password TEXT NOT NULL,
            role TEXT NOT NULL DEFAULT 'user',
            created_at DATETIME DEFAULT (datetime('now', '+8 hours'))
        )
    """)
    
    # 测试用户 (密码123456)
    admin_password = bcrypt.hashpw(b"123456", bcrypt.gensalt()).decode()
    user_password = bcrypt.hashpw(b"123456", bcrypt.gensalt()).decode()
    
    cursor.execute("""
        INSERT OR IGNORE INTO users (username, password, role) VALUES 
        ('admin', ?, 'admin'),
        ('user1', ?, 'user')
    """, (admin_password, user_password))
    
    # 资产报告表
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS reports (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            asset_number TEXT,
            asset_name TEXT NOT NULL,
            category_main TEXT NOT NULL,
            category_sub TEXT NOT NULL,
            unit TEXT NOT NULL,
            value DECIMAL(10,2) NOT NULL,
            status TEXT NOT NULL,
            department TEXT NOT NULL,
            location TEXT NOT NULL,
            service_life INTEGER,
            depreciation_rate DECIMAL(5,2),
            purchase_date TEXT,
            manufacturer TEXT,
            model TEXT,
            description TEXT,
            images TEXT,
            created_at DATETIME DEFAULT (datetime('now', '+8 hours')),
            FOREIGN KEY(user_id) REFERENCES users(id)
        )
    """)
    
    conn.commit()
    conn.close()

init_db()

# ========== 文件上传处理 (与Express multer配置完全一致) ==========
async def handle_upload(files: List[UploadFile]):
    saved_files = []
    for file in files:
        # 1MB 大小限制
        if file.size and file.size > 1024 * 1024:
            continue
        
        # 允许的图片类型
        if file.content_type not in ["image/jpeg", "image/png", "image/gif"]:
            continue
        
        # 生成唯一文件名 (与Express保持一致的命名规则)
        unique_suffix = f"{uuid.uuid4().hex}"
        ext = os.path.splitext(file.filename)[1]
        filename = f"{unique_suffix}{ext}"
        filepath = UPLOAD_DIR / filename
        
        # 保存文件
        with open(filepath, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
        
        saved_files.append(filename)
    
    return saved_files

# ========== 页面路由  ==========
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
    if get_session(request):
        return RedirectResponse("/dashboard")
    return FileResponse(BASE_DIR / "public/index.html")

@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request):
    if get_session(request):
        return RedirectResponse("/dashboard")
    return FileResponse(BASE_DIR / "public/index.html")

@app.get("/dashboard", response_class=HTMLResponse)
async def dashboard(request: Request):
    require_login(request)
    return FileResponse(BASE_DIR / "public/dashboard.html")

@app.get("/report", response_class=HTMLResponse)
async def report_page(request: Request):
    require_login(request)
    return FileResponse(BASE_DIR / "public/report.html")

@app.get("/admin/users", response_class=HTMLResponse)
async def admin_users(request: Request):
    require_admin(request)
    return FileResponse(BASE_DIR / "public/users.html")

# ========== API路由 - 认证相关  ==========
@app.post("/login")
async def login(request: Request):
    form_data = await request.form()
    username = form_data.get("username")
    password = form_data.get("password")
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
    user = cursor.fetchone()
    conn.close()
    
    if not user:
        raise HTTPException(status_code=401, detail="用户名或密码错误")
    
    if not bcrypt.checkpw(password.encode(), user[2].encode()):
        raise HTTPException(status_code=401, detail="用户名或密码错误")
    
    session_id = create_session({
        "id": user[0],
        "username": user[1],
        "role": user[3]
    })
    
    response = RedirectResponse("/dashboard", status_code=302)
    response.set_cookie(key="session_id", value=session_id)
    return response

@app.post("/api/users")
async def create_user(
    request: Request,
    username: str = Body(...),
    password: str = Body(...),
    role: str = Body(...)
):
    session = require_admin(request)
    
    if not all([username, password, role]):
        raise HTTPException(status_code=400, detail="用户名、密码和角色都是必填的")
    
    hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    try:
        cursor.execute(
            "INSERT INTO users (username, password, role) VALUES (?, ?, ?)",
            (username, hashed_password, role)
        )
        user_id = cursor.lastrowid
        conn.commit()
        return JSONResponse({"success": True, "userId": user_id})
    except sqlite3.IntegrityError:
        raise HTTPException(status_code=400, detail="用户名已存在")
    finally:
        conn.close()

@app.put("/api/users/{user_id}")
async def update_user(
    request: Request,
    user_id: int,
    username: str = Body(...),
    password: Optional[str] = Body(None),
    role: str = Body(...)
):
    session = require_admin(request)
    
    if not username or not role:
        raise HTTPException(status_code=400, detail="用户名和角色是必填的")
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    
    try:
        if password:
            hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
            cursor.execute(
                "UPDATE users SET username = ?, password = ?, role = ? WHERE id = ?",
                (username, hashed_password, role, user_id)
            )
        else:
            cursor.execute(
                "UPDATE users SET username = ?, role = ? WHERE id = ?",
                (username, role, user_id)
            )
        
        conn.commit()
        return JSONResponse({"success": True})
    except sqlite3.IntegrityError:
        raise HTTPException(status_code=400, detail="用户名已存在")
    finally:
        conn.close()

@app.get("/logout")
async def logout(request: Request):
    session_id = request.cookies.get("session_id")
    if session_id in sessions:
        delete_session(session_id)
    
    response = RedirectResponse("/", status_code=302)
    response.delete_cookie("session_id")
    return response

# ========== API路由 - 用户管理  ==========
@app.get("/api/users")
async def get_users(request: Request):
    session = require_admin(request)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("SELECT id, username, role, created_at FROM users ORDER BY created_at DESC")
    users = cursor.fetchall()
    conn.close()
    
    return [
        {
            "id": user[0],
            "username": user[1],
            "role": user[2],
            "created_at": user[3]
        }
        for user in users
    ]

@app.get("/api/users/{user_id}")
async def get_user(request: Request, user_id: int):
    session = require_admin(request)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("SELECT id, username, role FROM users WHERE id = ?", (user_id,))
    user = cursor.fetchone()
    conn.close()
    
    if not user:
        raise HTTPException(status_code=404, detail="用户不存在")
    
    return {
        "id": user[0],
        "username": user[1],
        "role": user[2]
    }

@app.delete("/api/users/{user_id}")
async def delete_user(request: Request, user_id: int):
    session = require_admin(request)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("DELETE FROM users WHERE id = ?", (user_id,))
    conn.commit()
    conn.close()
    
    return JSONResponse({"success": True})

# ========== API路由 - 资产报告  ==========


@app.get("/api/reports/export")
async def export_reports(request: Request):
    session = require_login(request)
    params = dict(request.query_params)
    
    start_date = params.get("start_date")
    end_date = params.get("end_date")
    status_filter = params.get("status")
    department = params.get("department")
    category_filter = params.get("category")  # 新增大类筛选参数
    
    # 解码URL参数
    if status_filter:
        status_filter = unquote(status_filter)
    if department:
        department = unquote(department)
    if category_filter:  # 解码大类参数
        category_filter = unquote(category_filter)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    
    base_query = """
        SELECT r.*, u.username 
        FROM reports r 
        JOIN users u ON r.user_id = u.id
    """
    
    conditions = []
    query_params = []
    
    if session["role"] != "admin":
        conditions.append("r.user_id = ?")
        query_params.append(session["id"])
    
    if start_date and end_date:
        conditions.append("date(r.created_at) BETWEEN date(?) AND date(?)")
        query_params.extend([start_date, end_date])
    
    if status_filter and status_filter != "全部":
        conditions.append("r.status = ?")
        query_params.append(status_filter)
    
    if department and department.strip():
        conditions.append("r.department LIKE ?")
        query_params.append(f"%{department.strip()}%")
    
    if category_filter and category_filter != "全部":  # 添加大类筛选条件
        conditions.append("r.category_main = ?")
        query_params.append(category_filter)
    
    if conditions:
        base_query += " WHERE " + " AND ".join(conditions)
    
    base_query += " ORDER BY r.created_at DESC"
    cursor.execute(base_query, query_params)
    reports = cursor.fetchall()
    conn.close()
    
    processed_reports = []
    for report in reports:
        images = report[17].split(",") if report[17] else []
        processed_reports.append({
            "id": report[0],
            "user_id": report[1],
            "asset_number": report[2],
            "asset_name": report[3],
            "category_main": report[4] or "未分类",
            "category_sub": report[5],
            "unit": report[6],
            "value": report[7],
            "status": report[8] or "正常",
            "department": report[9] or "未知部门",
            "location": report[10],
            "service_life": report[11],
            "depreciation_rate": report[12],
            "purchase_date": report[13],
            "manufacturer": report[14],
            "model": report[15],
            "description": report[16] or "",
            "images": [f"/uploads/{img.strip()}" for img in images if img.strip()],
            "username": report[19],
            "created_at": report[18]
        })
    
    return JSONResponse({
        "success": True,
        "data": processed_reports
    })

@app.get("/api/reports")
async def get_reports(request: Request):
    session = require_login(request)
    params = dict(request.query_params)
    
    # 解析分页参数
    page = int(params.get("page", 1))
    page_size = int(params.get("pageSize", 50))
    page = max(1, page)
    page_size = min(max(1, page_size), 100)
    offset = (page - 1) * page_size
    
    # 解析过滤参数
    start_date = params.get("start_date")
    end_date = params.get("end_date")
    status_filter = params.get("status")
    department = params.get("department")
    category_filter = params.get("category")  # 新增category参数
    
    # 解码URL参数
    if status_filter:
        status_filter = unquote(status_filter)
    if department:
        department = unquote(department)
    if category_filter:  # 解码category参数
        category_filter = unquote(category_filter)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    
    base_query = """
        SELECT r.*, u.username 
        FROM reports r 
        JOIN users u ON r.user_id = u.id
    """
    count_query = "SELECT COUNT(*) as total FROM reports r"
    
    conditions = []
    query_params = []
    
    # 权限过滤 (非管理员只能看自己的)
    if session["role"] != "admin":
        conditions.append("r.user_id = ?")
        query_params.append(session["id"])
    
    # 日期范围过滤
    if start_date and end_date:
        conditions.append("date(r.created_at) BETWEEN date(?) AND date(?)")
        query_params.extend([start_date, end_date])
    
    # 状态过滤
    if status_filter and status_filter != "全部":
        conditions.append("r.status = ?")
        query_params.append(status_filter)
    
    # 部门模糊查询
    if department and department.strip():
        conditions.append("r.department LIKE ?")
        query_params.append(f"%{department.strip()}%")
    
    # 新增分类过滤 - 当category存在且不为"全部"时
    if category_filter and category_filter != "全部":
        conditions.append("r.category_main = ?")
        query_params.append(category_filter.strip())
    
    # 组合查询条件
    if conditions:
        where_clause = " WHERE " + " AND ".join(conditions)
        base_query += where_clause
        count_query += where_clause
    
    # 获取总数
    cursor.execute(count_query, query_params)
    count_result = cursor.fetchone()
    total = count_result[0] if count_result else 0
    total_pages = (total + page_size - 1) // page_size
    
    # 获取分页数据
    data_query = base_query + " ORDER BY r.created_at DESC LIMIT ? OFFSET ?"
    data_params = query_params + [page_size, offset]
    
    cursor.execute(data_query, data_params)
    reports = cursor.fetchall()
    conn.close()
    
    # 处理返回数据
    processed_reports = []
    for report in reports:
        images = report[17].split(",") if report[17] else []
        processed_reports.append({
            "id": report[0],
            "user_id": report[1],
            "asset_number": report[2],
            "asset_name": report[3],
            "category_main": report[4] or "未分类",
            "category_sub": report[5],
            "unit": report[6],
            "value": report[7],
            "status": report[8] or "正常",
            "department": report[9] or "未知部门",
            "location": report[10],
            "service_life": report[11],
            "depreciation_rate": report[12],
            "purchase_date": report[13],
            "manufacturer": report[14],
            "model": report[15],
            "description": report[16] or "",
            "images": [f"/uploads/{img.strip()}" for img in images if img.strip()],
            "username": report[19],
            "created_at": report[18]
        })
    
    return JSONResponse({
        "success": True,
        "pagination": {
            "total": total,
            "totalPages": total_pages,
            "currentPage": page,
            "pageSize": page_size
        },
        "data": processed_reports
    })



@app.get("/api/reports/{report_id}")
async def get_report(request: Request, report_id: int):
    session = require_login(request)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("""
        SELECT r.*, u.username 
        FROM reports r 
        JOIN users u ON r.user_id = u.id 
        WHERE r.id = ?
    """, (report_id,))
    report = cursor.fetchone()
    conn.close()
    
    if not report:
        raise HTTPException(status_code=404, detail="报告不存在")
    
    # 权限检查
    if session["role"] != "admin" and report[1] != session["id"]:
        raise HTTPException(status_code=403, detail="无权访问此报告")
    
    # 处理图片路径
    images = []
    if report[17]:
        images = [f"/uploads/{img}" for img in report[17].split(",")]
    
    return JSONResponse({
        "id": report[0],
        "asset_number": report[2],
        "asset_name": report[3],
        "category_main": report[4],
        "category_sub": report[5],
        "unit": report[6],
        "value": report[7],
        "status": report[8],
        "department": report[9],
        "location": report[10],
        "service_life": report[11],
        "depreciation_rate": report[12],
        "purchase_date": report[13],
        "manufacturer": report[14],
        "model": report[15],
        "description": report[16],
        "images": images,
        "username": report[19],
        "created_at": report[18]
    })

@app.post("/api/reports")
async def create_report(
    request: Request,
    asset_name: str = Form(...),
    category_main: str = Form(...),
    category_sub: str = Form(""),
    unit: str = Form(...),
    value: float = Form(...),
    status: str = Form("正常"),
    department: str = Form(...),
    location: str = Form(...),
    service_life: Optional[int] = Form(None),
    depreciation_rate: Optional[float] = Form(None),
    purchase_date: str = Form(""),
    manufacturer: str = Form(""),
    model: str = Form(""),
    description: str = Form(""),
    images: List[UploadFile] = File([])  # 与前端保持一致的字段名
):
    session = require_login(request)
    
    # 验证必填字段
    if not all([asset_name, category_main, unit, value, department, location]):
        raise HTTPException(status_code=400, detail="所有必填字段都是必填的")
    
    # 处理文件上传
    saved_files = await handle_upload(images)
    images_str = ",".join(saved_files)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("""
        INSERT INTO reports (
            user_id, asset_name, category_main, category_sub, 
            unit, value, status, department, location, 
            service_life, depreciation_rate, purchase_date, 
            manufacturer, model, description, images
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    """, (
        session["id"],
        asset_name,
        category_main,
        category_sub,
        unit,
        value,
        status,
        department,
        location,
        service_life,
        depreciation_rate,
        purchase_date,
        manufacturer,
        model,
        description,
        images_str
    ))
    report_id = cursor.lastrowid
    conn.commit()
    conn.close()
    
    return JSONResponse({"success": True, "reportId": report_id})



@app.put("/api/reports/{report_id}")
async def update_report(
    request: Request,
    report_id: int,
    asset_number: Optional[str] = Form(None),
    asset_name: str = Form(...),
    category_main: str = Form(...),
    category_sub: str = Form(""),
    unit: str = Form(...),
    value: float = Form(...),
    status: str = Form("正常"),
    department: str = Form(...),
    location: str = Form(...),
    service_life: Optional[int] = Form(None),
    depreciation_rate: Optional[float] = Form(None),
    purchase_date: str = Form(""),
    manufacturer: str = Form(""),
    model: str = Form(""),
    description: str = Form(""),
    existing_images: str = Form(""),  # 修改为接受空字符串
    images_to_delete: str = Form(""),
    images: List[UploadFile] = File([])
):
    session = require_login(request)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM reports WHERE id = ?", (report_id,))
    report = cursor.fetchone()
    
    if not report:
        conn.close()
        raise HTTPException(status_code=404, detail="报告不存在")
    
    # 权限检查
    if session["role"] != "admin" and report[1] != session["id"]:
        conn.close()
        raise HTTPException(status_code=403, detail="无权修改此报告")
    
    # 验证必填字段
    if not all([asset_name, category_main, unit, value, department, location]):
        conn.close()
        raise HTTPException(status_code=400, detail="所有必填字段都是必须的")
    
    # 处理要删除的图片
    images_to_delete_list = []
    if images_to_delete:
        images_to_delete_list = [img.strip() for img in images_to_delete.split(",") if img.strip()]
    
    # 处理现有图片 - 明确处理空字符串情况
    images_to_keep = []
    if existing_images:  # 即使为空字符串也会处理
        images_to_keep = [img.strip() for img in existing_images.split(",") if img.strip()]
    
    # 处理新上传的图片
    saved_files = await handle_upload(images)
    images_to_keep.extend(saved_files)
    
    # 如果没有图片且原有记录有图片，则保留原有图片
    if not images_to_keep and report[17]:
        images_to_keep = report[17].split(",")
    
    images_str = ",".join(images_to_keep)
    
    # 更新数据库
    cursor.execute("""
        UPDATE reports SET
            asset_number = ?,
            asset_name = ?,
            category_main = ?,
            category_sub = ?,
            unit = ?,
            value = ?,
            status = ?,
            department = ?,
            location = ?,
            service_life = ?,
            depreciation_rate = ?,
            purchase_date = ?,
            manufacturer = ?,
            model = ?,
            description = ?,
            images = ?
        WHERE id = ?
    """, (
        asset_number or report[2],  # 保留原有asset_number如果未提供
        asset_name,
        category_main,
        category_sub,
        unit,
        value,
        status,
        department,
        location,
        service_life,
        depreciation_rate,
        purchase_date,
        manufacturer,
        model,
        description,
        images_str,
        report_id
    ))
    
    conn.commit()
    conn.close()
    
    # 处理图片删除 - 在所有数据库操作完成后执行
    if images_to_delete_list:
        for filename in images_to_delete_list:
            image_path = UPLOAD_DIR / filename
            if image_path.exists() and image_path.is_file():
                try:
                    os.remove(image_path)
                    print(f"成功删除图片: {image_path}")
                except Exception as e:
                    print(f"删除图片失败: {image_path}, 错误: {str(e)}")
    
    return JSONResponse({
        "success": True,
        "message": "报告更新成功",
        "images": [f"/uploads/{img}" for img in images_to_keep if img]
    })


@app.delete("/api/reports/{report_id}")
async def delete_report(request: Request, report_id: int):
    session = require_admin(request)
    
    conn = sqlite3.connect(DB_FILE)
    cursor = conn.cursor()
    cursor.execute("SELECT images FROM reports WHERE id = ?", (report_id,))
    report = cursor.fetchone()
    
    if not report:
        conn.close()
        raise HTTPException(status_code=404, detail="记录不存在")
    
    # 删除数据库记录
    cursor.execute("DELETE FROM reports WHERE id = ?", (report_id,))
    deleted_rows = cursor.rowcount
    conn.commit()
    conn.close()
    
    # 处理图片删除
    if report[0]:  # 检查是否有图片记录
        image_files = [img.strip() for img in report[0].split(",") if img.strip()]
        
        for filename in image_files:
            # 构建完整的图片路径（处理文件名中的特殊字符）
            image_path = UPLOAD_DIR / filename
            
            # 验证文件存在性后删除
            if image_path.exists() and image_path.is_file():
                try:
                    os.remove(image_path)  # 使用os.remove更可靠
                    print(f"成功删除图片: {image_path}")
                except Exception as e:
                    print(f"删除图片失败: {image_path}, 错误: {str(e)}")
            else:
                print(f"图片文件不存在: {image_path}")
    
    return JSONResponse({
        "success": True,
        "message": "报告及关联图片已成功删除",
        "deletedRows": deleted_rows
    })


# ========== 启动服务器  ==========
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=PORT)